﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Dubbo.Common.Utils
{
    public sealed class NetUtils
    {
        public const string LOCALHOST = "127.0.0.1";

        public const string ANYHOST = "0.0.0.0";

        private const int MIN_PORT = 0;

        private const int MAX_PORT = 65535;

        private const int RND_PORT_START = 30000;

        private const int RND_PORT_RANGE = 10000;

        private static Random RANDOM = new Random((int)DateTime.Now.Ticks);



        public static int GetRandomPort()
        {
            return RND_PORT_START + RANDOM.Next(RND_PORT_RANGE);
        }

        public static int GetAvailablePort()
        {
            Socket socket = null;
            try
            {
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //socket.Bind(new IPEndPoint(IPAddress.Any, port));
                var point = (IPEndPoint)socket.LocalEndPoint;
                socket.Listen(10);
                return point.Port;
            }
            catch (Exception ex)
            {
                return GetRandomPort();
            }
            finally
            {
                if (socket != null)
                {
                    socket.Close();
                }
            }
        }

        public static int GetAvailablePort(int port)
        {
            if (port <= 0)
            {
                return GetAvailablePort();
            }
            for (int i = port; i < MAX_PORT; i++)
            {
                Socket socket = null;
                try
                {
                    socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    socket.Bind(new IPEndPoint(IPAddress.Any, i));
                    return i;
                }
                catch (Exception e)
                {
                }
                finally
                {
                    if (socket != null)
                    {
                        socket.Close();
                    }
                }
            }
            return port;
        }

        private static readonly Regex LOCAL_IP_PATTERN = new Regex("127(\\.\\d{1,3}){3}$");

        public static bool IsInvalidLocalHost(string host)
        {
            return string.IsNullOrEmpty(host)
                        || host.ToLower() == "localhost"
                        || host == "0.0.0.0"
                        || (LOCAL_IP_PATTERN.IsMatch(host));
        }

        private static readonly Regex IP_PATTERN = new Regex("\\d{1,3}(\\.\\d{1,3}){3,5}$");

        private static bool IsValidAddress(IPAddress address)
        {
            if (address == null)
            {
                return false;
            }
            string name = address.ToString();
            return ANYHOST != name
                    && LOCALHOST != name
                    && IP_PATTERN.IsMatch(name);
        }

        public static IPAddress GetLocalIPAddress()
        {
            var host = Dns.GetHostEntry(Dns.GetHostName());
            return host.AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
        }
        
        public static string GetLocalHost()
        {
            IPAddress address = GetLocalAddress();
            return address == null ? LOCALHOST : address.ToString();
        }

        private static volatile IPAddress LOCAL_ADDRESS = null;

        /// <summary>
        /// 遍历本地网卡，返回第一个合理的IP。
        /// </summary>
        /// <returns>本地网卡IP</returns>
        public static IPAddress GetLocalAddress()
        {
            if (LOCAL_ADDRESS != null)
            {
                return LOCAL_ADDRESS;
            }
            IPAddress localAddress = GetLocalAddress0();
            LOCAL_ADDRESS = localAddress;
            return localAddress;
        }

        private static IPAddress GetLocalAddress0()
        {
            IPAddress localAddress = null;
            try
            {
                var host = Dns.GetHostEntry(Dns.GetHostName());
                localAddress = host.AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
                if (IsValidAddress(localAddress))
                {
                    return localAddress;
                }
            }
            catch (Exception e)
            {
                //logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
            }
            try
            {
                foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces())
                {
                    if (item.NetworkInterfaceType == NetworkInterfaceType.Ethernet && item.OperationalStatus == OperationalStatus.Up)
                    {
                        foreach (UnicastIPAddressInformation ip in item.GetIPProperties().UnicastAddresses)
                        {
                            if (ip.Address.AddressFamily == AddressFamily.InterNetwork && IsValidAddress(ip.Address))
                            {
                                return ip.Address;
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                //logger.warn("Failed to retriving ip address, " + e.getMessage(), e);
            }
            //logger.error("Could not get local host ip address, will use 127.0.0.1 instead.");
            return localAddress;
        }
    }
}
